﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using haptic.src.engine.debug;
using System.Diagnostics;

namespace haptic.src.engine.maths
{
    [Serializable]
    public struct Mat2x3 : IXmlSerializable
    {
        #region Constructors
        /*public Mat2x3()
        {
            I.X = float.NaN;
            I.Y = float.NaN;
            J.X = float.NaN;
            J.Y = float.NaN;
            O.X = float.NaN;
            O.Y = float.NaN;
        }*/
        public Mat2x3(float Ix, float Iy, float Jx, float Jy, float Ox, float Oy)
        {
            I.X = Ix;
            I.Y = Iy;
            J.X = Jx;
            J.Y = Jy;
            O.X = Ox;
            O.Y = Oy;
        }
        public Mat2x3(Vector2 _I, Vector2 _J, Vector2 _O)
        {
            I = _I;
            J = _J;
            O = _O;
        }
        public Mat2x3(float fAngle, Vector2 _O)
        {
            float fCos = (float)Math.Cos((double)fAngle);
            float fSin = (float)Math.Sin((double)fAngle);
            I.X = fCos;
            I.Y = fSin;
            J.X = -fSin;
            J.Y = fCos;
            O = _O;
        }
        #endregion

        #region Operators
        public static Mat2x3 operator *(float fScale, Mat2x3 mMat)
        {
            return new Mat2x3(mMat.I * fScale, mMat.J * fScale, mMat.O * fScale);
        }
        public static Mat2x3 operator *(Mat2x3 mMat, float fScale)
        {
            return new Mat2x3(mMat.I * fScale, mMat.J * fScale, mMat.O * fScale);
        }
        public static Mat2x3 operator *(Mat2x3 m1, Mat2x3 m2)
        {
            return new Mat2x3(
                m1.I.X * m2.I.X + m1.J.X * m2.I.Y, m1.I.Y * m2.I.X + m1.J.Y * m2.I.Y,
                m1.I.X * m2.J.X + m1.J.X * m2.J.Y, m1.I.Y * m2.J.X + m1.J.Y * m2.J.Y,
                m1.I.X * m2.O.X + m1.J.X * m2.O.Y + m1.O.X, m1.I.Y * m2.O.X + m1.J.Y * m2.O.Y + m1.O.Y
                );
        }
        #endregion

        #region Factory
        public static Mat2x3 CreateTranslation(Vector2 vO)
        {
            return new Mat2x3(Vector2.UnitX, Vector2.UnitY, vO);
        }
        #endregion

        public void SetIdentity()
        {
            I.X = 1.0f;
            I.Y = 0.0f;
            J.X = 0.0f;
            J.Y = 1.0f;
            O.X = 0.0f;
            O.Y = 0.0f;
        }

        #region Serialization
        public XmlSchema GetSchema()
        {
            return null;
        }
        public void WriteXml(XmlWriter w)
        {
            if (!IsValid())
            {
                Log.Error("invalid matrix written");
            }
            string sVal = (float.IsNaN(I.X) ? "NaN" : I.X.ToString()) + " "
                + (float.IsNaN(I.Y) ? "NaN" : I.Y.ToString()) + " "
                + (float.IsNaN(J.X) ? "NaN" : J.X.ToString()) + " "
                + (float.IsNaN(J.Y) ? "NaN" : J.Y.ToString()) + " "
                + (float.IsNaN(O.X) ? "NaN" : O.X.ToString()) + " "
                + (float.IsNaN(O.Y) ? "NaN" : O.Y.ToString());
            w.WriteValue(sVal);
        }
        public void ReadXml(XmlReader r)
        {
            string sVal = r.ReadString();
            string[] sVals = sVal.Split(' ');
            if (sVals.Length != 6)
            {
                Log.Error("invalid matrix in xml file: " + sVal);
                SetIdentity();
            }
            else
            {
                I.X = (sVals[0] == "NaN") ? float.NaN : float.Parse(sVals[0]);
                I.Y = (sVals[1] == "NaN") ? float.NaN : float.Parse(sVals[1]);
                J.X = (sVals[2] == "NaN") ? float.NaN : float.Parse(sVals[2]);
                J.Y = (sVals[3] == "NaN") ? float.NaN : float.Parse(sVals[3]);
                O.X = (sVals[4] == "NaN") ? float.NaN : float.Parse(sVals[4]);
                O.Y = (sVals[5] == "NaN") ? float.NaN : float.Parse(sVals[5]);
            }
        }
        #endregion

        public bool IsValid()
        {
            if (float.IsNaN(I.X) || float.IsInfinity(I.X)) return false;
            if (float.IsNaN(I.Y) || float.IsInfinity(I.Y)) return false;
            if (float.IsNaN(J.X) || float.IsInfinity(J.X)) return false;
            if (float.IsNaN(J.Y) || float.IsInfinity(J.Y)) return false;
            if (float.IsNaN(O.X) || float.IsInfinity(O.X)) return false;
            if (float.IsNaN(O.Y) || float.IsInfinity(O.Y)) return false;

            return true;
        }

        #region Constants
        public static Mat2x3 Identity = new Mat2x3(1, 0, 0, 1, 0, 0);
        #endregion

        #region Attributes
        public Vector2 I;
        public Vector2 J;
        public Vector2 O;
        #endregion
    }
}
